package id.co.gomobile.assessment

import java.util.Calendar;

import org.joda.time.DateTime;
import org.joda.time.Period;

class AssessmentService {

    boolean transactional = true
	
	/**
	 * TODO: Improve the performance, large sets (but smaller size of items required) of items may reduce the assignment creation process
	 * TODO: Move this?
	 */
    def createAssignment(Assignment assignment) {
		if(assignment.save(flush: true)) {
			// prepare 
			def items = Item.findAllByTest(assignment.test)
			
			// shuffle first
			Collections.shuffle(items, new Random())

			def totalItems = items.size > assignment.test.preference.maxQuestions ? assignment.test.preference.maxQuestions : items.size
			def i = 0
			for(item in items) {
				if(i >= totalItems) break
				else i++
				
				def assignmentItem = new AssignmentItem()
				assignmentItem.item = item
				assignmentItem.assignment = assignment 
				
				assignmentItem.save(flush: true)
			}
			
			return assignment
		}
		
		return null
    }
	
	/**
	 * Get total points
	 * 
	 * @param assignment
	 * @return
	 */
	def getAssignmentPoint(Assignment assignment) {
		def point = 0
		for(item in assignment.items) {
			point += getAssignmentItemPoint(item)
		}
		return point
	}
	
	def getAssignmentCategoryPoint(Assignment assignment) {
		def point = [:]
		for(item in assignment.items) {
			def catPoint = point.get(item.item.category) ? point.get(item.item.category) : [:]
			
			def total = catPoint.total ? catPoint.total : 0
			if(assignment.endTime) { // only calculate if assignment hadbeen finished, otherwise give it 0
				total += getAssignmentItemPoint(item).asType(int)
			} else {
				total += 0			
			}
			def of = catPoint.of ? catPoint.of : 0
			of += 1
						
			catPoint.total = total
			catPoint.of = of

			point.put(item.item.category, catPoint)
		}
		return point
	}
	
	/**
	 * Get item point
	 * 
	 * Correct: 1
	 * Incorrect: 0
	 * No Answer for No Answer Item : 1 else 0
	 * 
	 * @param assignmentItem
	 * @return
	 */
	def getAssignmentItemPoint(AssignmentItem assignmentItem) {
		def answers = assignmentItem.answers
		def correctItems = ItemOption.findAllByItemAndCorrect(assignmentItem.item, true)
		
		if(assignmentItem.item.type() == Item.SINGLE_ANSWER || assignmentItem.item.type() == Item.MULTIPLE_ANSWER) {
			int totalCorrectPoint = correctItems.size()
			int totalCorrect = 0
			boolean correct = true
			for(answer in answers) {
				if(answer.correct) {
					totalCorrect += 1
				} else {
					correct = false
				}
			}
			double total = correct && (totalCorrectPoint == answers.size()) ? 1 : 0 
			
			return total
		} else if(assignmentItem.item.type() == Item.NO_ANSWER) {
			if(answers.size() == 0) {
				// 1 point for correctly guessing that the question have no answer :)
				return 1
				//return 0
			} else {
				// thats wrong answer!
				return 0
			}
		} else {
			// currently unanswered item would be 0
			return 0
		}
	}
	
	/**
	 * Start assignment, setup startTime etc 
	 * 
	 * @param assignment
	 * @return
	 */
	def startAssignment(Assignment assignment) {
		Calendar s = Calendar.getInstance()
		Calendar ee = s.clone()
		ee.add(Calendar.MINUTE, assignment.test.preference.timeout)

		assignment.startTime = s.getTime()
		assignment.expectedEndTime = ee.getTime()

		assignment.save(flush: true)
	}
	
	/**
	 * Get items displayed per page
	 * 
	 * @param assignment
	 * @param page
	 * @return
	 */
	def getPage(Assignment assignment, int page) {
		def itemPerPage = assignment.test.preference.maxQuestionsPerPage
		
		def maxPage = Math.ceil(assignment.items.size() / assignment.test.preference.maxQuestionsPerPage).asType(int)
		
		if(page >= maxPage) {
			page = 0 // reset page to 0
		}
		
		def offset = page * itemPerPage 
		def items = AssignmentItem.findAllByAssignment(assignment, [max: itemPerPage, offset: offset, sort: "id", order: "asc"])
	}
	
	
	/**
	 * Get remaining time
	 * 
	 * @param assignment
	 * @return
	 */
	def getRemainingTime(Assignment assignment) {
		if(assignment.expectedEndTime) {
			def current = new Date()
			
			DateTime expectedEndTimeJoda = new DateTime(assignment.expectedEndTime.getTime())
			DateTime currentJoda = new DateTime(current.getTime())
			
			Period period = new Period(currentJoda, expectedEndTimeJoda)
			return [hours: period.getHours(),  minutes: period.getMinutes(), seconds: period.getSeconds()]
		}
		
		return [hours: 0,  minutes: 0, seconds: 0]
	}
	
	/**
	 * Check if game is timed out
	 * 
	 * @param assignment
	 * @return
	 */
	def isTimeout(Assignment assignment) {
		if(assignment.test.preference.forceSubmitOnTimeout) {
			def remaining = this.getRemainingTime(assignment)
			return (remaining.hours < 0 || remaining.minutes < 0 || remaining.minutes < 0)
		} else {
			return false
		}
	}

}
